import tkinter as tk
from tkinter import ttk, messagebox
import sqlite3
from cryptography.fernet import Fernet
import os
import random
import string
import sys
import platform

class PasswordManager:
    def __init__(self, root):
        self.root = root
        self.root.title("密码管理器 - 修复版")
        self.root.geometry("900x600")
        self.root.resizable(True, True)
        
        # 初始化数据目录和路径（解决环境一致性问题）
        self.init_paths()
        
        # 初始化数据库和加密密钥
        self.init_database()
        self.init_encryption()
        
        # 创建主界面
        self.create_widgets()
        
        # 用于存储分类ID的字典
        self.category_ids = {}
        
        # 加载数据
        self.load_categories()
        self.load_passwords()
        
        # 显示当前环境信息（用于调试）
        self.show_environment_info()
    
    def init_paths(self):
        """初始化应用所需的路径，确保在不同环境下一致"""
        # 获取应用数据目录
        if platform.system() == 'Windows':
            self.app_data_dir = os.path.join(os.environ.get('APPDATA', ''), 'PasswordManager')
        elif platform.system() == 'Darwin':  # macOS
            self.app_data_dir = os.path.join(os.path.expanduser('~'), 'Library', 'Application Support', 'PasswordManager')
        else:  # Linux
            self.app_data_dir = os.path.join(os.path.expanduser('~'), '.password_manager')
        
        # 确保目录存在
        if not os.path.exists(self.app_data_dir):
            os.makedirs(self.app_data_dir)
        
        # 数据库和密钥文件路径
        self.db_path = os.path.join(self.app_data_dir, 'passwords.db')
        self.key_file = os.path.join(self.app_data_dir, 'encryption_key.key')
    
    def init_encryption(self):
        """初始化加密密钥，如果不存在则创建"""
        if not os.path.exists(self.key_file):
            # 生成新密钥
            key = Fernet.generate_key()
            with open(self.key_file, "wb") as key_file:
                key_file.write(key)
        
        # 加载密钥
        try:
            with open(self.key_file, "rb") as key_file:
                self.key = key_file.read()
            self.cipher_suite = Fernet(self.key)
        except Exception as e:
            messagebox.showerror("加密初始化错误", f"无法加载加密密钥: {str(e)}")
            self.root.destroy()
    
    def init_database(self):
        """初始化数据库，支持二级分类"""
        try:
            self.conn = sqlite3.connect(self.db_path)
            self.cursor = self.conn.cursor()
            
            # 创建分类表，添加parent_id支持二级分类
            self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS categories (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL,
                parent_id INTEGER,
                FOREIGN KEY (parent_id) REFERENCES categories (id),
                UNIQUE(name, parent_id)
            )
            ''')
            
            # 创建密码表
            self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS passwords (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL,
                url TEXT,
                username TEXT,
                password BLOB NOT NULL,
                notes TEXT,
                category_id INTEGER,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (category_id) REFERENCES categories (id)
            )
            ''')
            
            # 仅在表为空时添加默认分类（解决重复添加问题）
            self.cursor.execute("SELECT COUNT(*) FROM categories")
            if self.cursor.fetchone()[0] == 0:
                self._add_default_categories()
            
            self.conn.commit()
        except Exception as e:
            messagebox.showerror("数据库初始化错误", f"无法初始化数据库: {str(e)}")
            self.root.destroy()
    
    def _add_default_categories(self):
        """添加默认的一级和二级分类"""
        default_categories = [
            # 一级分类
            ("工作", None),
            ("个人", None),
            ("金融", None),
            ("其他", None),
            # 二级分类
            ("公司邮箱", 1),  # 属于"工作"分类
            ("项目账号", 1),  # 属于"工作"分类
            ("社交媒体", 2),  # 属于"个人"分类
            ("购物网站", 2),  # 属于"个人"分类
            ("银行账户", 3),  # 属于"金融"分类
            ("支付平台", 3)   # 属于"金融"分类
        ]
        
        for name, parent_id in default_categories:
            try:
                self.cursor.execute(
                    'INSERT INTO categories (name, parent_id) VALUES (?, ?)', 
                    (name, parent_id)
                )
            except sqlite3.IntegrityError:
                # 分类已存在，忽略
                pass
    
    def create_widgets(self):
        """创建界面组件，包含二级分类支持"""
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 顶部控制区
        control_frame = ttk.Frame(main_frame, padding="5")
        control_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 分类选择区域
        category_frame = ttk.LabelFrame(control_frame, text="分类", padding="5")
        category_frame.pack(side=tk.LEFT, padx=(0, 10))
        
        # 分类树视图
        self.category_tree = ttk.Treeview(category_frame, height=10, show="tree", columns=("id",))
        self.category_tree.column("id", width=0, stretch=tk.NO)  # 隐藏ID列
        self.category_tree.pack(side=tk.LEFT)
        self.category_tree.bind("<<TreeviewSelect>>", lambda e: self.load_passwords())
        
        # 分类操作按钮
        category_buttons = ttk.Frame(category_frame, padding="5")
        category_buttons.pack(side=tk.LEFT, padx=5)
        
        ttk.Button(category_buttons, text="添加分类", command=self.add_category).pack(fill=tk.X, pady=2)
        ttk.Button(category_buttons, text="编辑分类", command=self.edit_category).pack(fill=tk.X, pady=2)
        ttk.Button(category_buttons, text="删除分类", command=self.delete_category).pack(fill=tk.X, pady=2)
        
        # 搜索框
        ttk.Label(control_frame, text="搜索:").pack(side=tk.LEFT, padx=(0, 5))
        self.search_var = tk.StringVar()
        search_entry = ttk.Entry(control_frame, textvariable=self.search_var, width=20)
        search_entry.pack(side=tk.LEFT, padx=(0, 10))
        search_entry.bind("<Return>", lambda e: self.load_passwords())
        
        # 密码操作按钮
        ttk.Button(control_frame, text="添加密码", command=self.add_password).pack(side=tk.LEFT, padx=2)
        ttk.Button(control_frame, text="编辑密码", command=self.edit_password).pack(side=tk.LEFT, padx=2)
        ttk.Button(control_frame, text="删除密码", command=self.delete_password).pack(side=tk.LEFT, padx=2)
        ttk.Button(control_frame, text="生成密码", command=self.generate_password).pack(side=tk.LEFT, padx=2)
        
        # 密码列表
        columns = ("id", "name", "url", "username", "category")
        self.tree = ttk.Treeview(main_frame, columns=columns, show="headings")
        
        # 设置列标题和宽度
        self.tree.heading("id", text="ID")
        self.tree.heading("name", text="名称")
        self.tree.heading("url", text="网址")
        self.tree.heading("username", text="用户名")
        self.tree.heading("category", text="分类")
        
        self.tree.column("id", width=50, anchor=tk.CENTER)
        self.tree.column("name", width=150)
        self.tree.column("url", width=200)
        self.tree.column("username", width=150)
        self.tree.column("category", width=150)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=self.tree.yview)
        self.tree.configure(yscroll=scrollbar.set)
        
        self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        # 双击查看密码
        self.tree.bind("<Double-1>", self.view_password)
    
    def load_categories(self):
        """加载分类到树形视图，支持层级显示"""
        # 清空现有分类
        for item in self.category_tree.get_children():
            self.category_tree.delete(item)
        
        # 清空分类ID字典
        self.category_ids.clear()
        
        # 添加"全部"选项
        self.all_item = self.category_tree.insert("", tk.END, text="全部", open=True)
        self.category_ids[self.all_item] = "all"
        
        try:
            # 查询所有一级分类（parent_id为NULL）
            self.cursor.execute("SELECT id, name FROM categories WHERE parent_id IS NULL ORDER BY name")
            main_categories = self.cursor.fetchall()
            
            # 为每个一级分类添加子分类
            for main_id, main_name in main_categories:
                # 添加一级分类
                main_item = self.category_tree.insert(self.all_item, tk.END, text=main_name, open=True)
                # 存储分类ID
                self.category_ids[main_item] = main_id
                
                # 查询并添加二级分类
                self.cursor.execute("SELECT id, name FROM categories WHERE parent_id = ? ORDER BY name", (main_id,))
                sub_categories = self.cursor.fetchall()
                
                for sub_id, sub_name in sub_categories:
                    sub_item = self.category_tree.insert(main_item, tk.END, text=sub_name)
                    # 存储分类ID
                    self.category_ids[sub_item] = sub_id
            
            # 默认选择"全部"
            self.category_tree.selection_set(self.all_item)
            return True
        except Exception as e:
            messagebox.showerror("加载分类错误", f"无法加载分类: {str(e)}")
            return False
    
    def get_selected_category_id(self):
        """获取选中的分类ID"""
        selected_items = self.category_tree.selection()
        if not selected_items:
            return None
            
        selected_item = selected_items[0]
        return self.category_ids.get(selected_item, "all")
    
    def load_passwords(self):
        # 清空现有项
        for item in self.tree.get_children():
            self.tree.delete(item)

        category_id = self.get_selected_category_id()
        search_term = self.search_var.get().lower()

        # 构建查询
        query = '''
        SELECT p.id, p.name, p.url, p.username, p.password, p.notes, p.category_id, 
               c1.name as category_name, c2.name as subcategory_name
        FROM passwords p
        LEFT JOIN categories c1 ON p.category_id = c1.id
        LEFT JOIN categories c2 ON c1.parent_id = c2.id
        WHERE 1=1
        '''
        params = []

        # 应用分类过滤
        if category_id and category_id != "all":
            # 只显示选中分类的密码，不包括子分类
            query += " AND p.category_id = ?"
            params.append(category_id)

        # 应用搜索过滤
        if search_term:
            query += '''
            AND (p.name LIKE ? OR p.url LIKE ? OR p.username LIKE ? OR p.notes LIKE ?)
            '''
            search_param = f"%{search_term}%"
            params.extend([search_param, search_param, search_param, search_param])

        # 按名称排序
        query += " ORDER BY p.name"

        try:
            self.cursor.execute(query, params)
            passwords = self.cursor.fetchall()

            for password_data in passwords:
                item_id = password_data[0]
                name = password_data[1]
                url = password_data[2]
                username = password_data[3]
                encrypted_password = password_data[4]
                notes = password_data[5]
                category_id = password_data[6]
                category_name = password_data[7]
                subcategory_name = password_data[8]

                # 显示分类路径
                if subcategory_name:
                    display_category = f"{subcategory_name} > {category_name}"
                else:
                    display_category = category_name

                # 添加到表格
                self.tree.insert('', tk.END, id=item_id, values=(
                    item_id, name, url, username, display_category
                ))

        except Exception as e:
            messagebox.showerror("错误", f"加载密码时出错: {str(e)}")
    
    def add_password(self):
        """添加新密码，支持选择二级分类"""
        # 创建添加密码窗口
        dialog = PasswordDialog(self.root, "添加密码", self)
        if dialog.result:
            name, url, username, password, notes, category_id = dialog.result
            
            # 加密密码
            encrypted_password = self.cipher_suite.encrypt(password.encode())
            
            # 插入数据库
            try:
                self.cursor.execute('''
                INSERT INTO passwords (name, url, username, password, notes, category_id)
                VALUES (?, ?, ?, ?, ?, ?)
                ''', (name, url, username, encrypted_password, notes, category_id))
                
                self.conn.commit()
                self.load_passwords()
                messagebox.showinfo("成功", "密码添加成功")
            except Exception as e:
                self.conn.rollback()
                messagebox.showerror("错误", f"添加密码失败: {str(e)}")
    
    def edit_password(self):
        """编辑选中的密码"""
        selected_item = self.tree.selection()
        if not selected_item:
            messagebox.showwarning("警告", "请先选择一个密码")
            return
        
        item_id = self.tree.item(selected_item[0])['values'][0]
        
        # 获取当前密码信息
        self.cursor.execute("SELECT * FROM passwords WHERE id = ?", (item_id,))
        password_data = self.cursor.fetchone()
        
        if not password_data:
            messagebox.showerror("错误", "未找到密码数据")
            return
        
        # 创建编辑窗口
        dialog = PasswordDialog(
            self.root, "编辑密码", self,
            name=password_data[1],
            url=password_data[2],
            username=password_data[3],
            password=self.cipher_suite.decrypt(password_data[4]).decode(),
            notes=password_data[5],
            category_id=password_data[6]
        )
        
        if dialog.result:
            name, url, username, password, notes, category_id = dialog.result
            
            # 加密新密码
            encrypted_password = self.cipher_suite.encrypt(password.encode())
            
            # 更新数据库
            try:
                self.cursor.execute('''
                UPDATE passwords
                SET name = ?, url = ?, username = ?, password = ?, notes = ?, category_id = ?, updated_at = CURRENT_TIMESTAMP
                WHERE id = ?
                ''', (name, url, username, encrypted_password, notes, category_id, item_id))
                
                self.conn.commit()
                self.load_passwords()
                messagebox.showinfo("成功", "密码更新成功")
            except Exception as e:
                self.conn.rollback()
                messagebox.showerror("错误", f"更新密码失败: {str(e)}")
    
    def delete_password(self):
        """删除选中的密码"""
        selected_item = self.tree.selection()
        if not selected_item:
            messagebox.showwarning("警告", "请先选择一个密码")
            return
        
        if messagebox.askyesno("确认", "确定要删除选中的密码吗？"):
            item_id = self.tree.item(selected_item[0])['values'][0]
            try:
                self.cursor.execute("DELETE FROM passwords WHERE id = ?", (item_id,))
                self.conn.commit()
                self.load_passwords()
                messagebox.showinfo("成功", "密码已删除")
            except Exception as e:
                self.conn.rollback()
                messagebox.showerror("错误", f"删除密码失败: {str(e)}")
    
    def view_password(self, event):
        """查看密码详情，包含单项复制功能"""
        selected_item = self.tree.selection()
        if not selected_item:
            return
        
        item_id = self.tree.item(selected_item[0])['values'][0]
        self.view_password_details(item_id)
    
    def view_password_details(self, item_id):
        # 添加调试信息
        print(f"查看ID为{item_id}的密码详情")
        
        # 查询数据库
        self.cursor.execute("""
        SELECT p.*, 
               CASE 
                   WHEN c2.name IS NOT NULL THEN c1.name || ' > ' || c2.name
                   ELSE c1.name
               END as category_path
        FROM passwords p
        LEFT JOIN categories c1 ON 
            (p.category_id = c1.id AND c1.parent_id IS NULL) OR 
            (p.category_id = c1.parent_id AND c1.parent_id IS NOT NULL)
        LEFT JOIN categories c2 ON 
            p.category_id = c2.id AND c2.parent_id IS NOT NULL
        WHERE p.id = ?
        """, (item_id,))
        
        data = self.cursor.fetchone()
        
        # 检查数据是否存在
        if not data:
            messagebox.showerror("错误", "未找到密码数据")
            return
        
        # 检查数据结构
        print(f"获取到的数据: {data}")
        if len(data) < 8:
            messagebox.showerror("错误", "数据格式不正确")
            return
        
        # 解密密码
        try:
            encrypted_password = data[4]
            decrypted_password = self.cipher_suite.decrypt(encrypted_password).decode()
        except Exception as e:
            messagebox.showerror("解密错误", f"无法解密密码: {str(e)}")
            return
        
        # 创建详情窗口
        detail_window = tk.Toplevel(self.root)
        detail_window.title("密码详情")
        detail_window.geometry("500x320")
        detail_window.resizable(False, False)
        detail_window.transient(self.root)
        detail_window.grab_set()
        
        # 居中显示
        detail_window.update_idletasks()
        width = detail_window.winfo_width()
        height = detail_window.winfo_height()
        x = (self.root.winfo_width() // 2) - (width // 2) + self.root.winfo_x()
        y = (self.root.winfo_height() // 2) - (height // 2) + self.root.winfo_y()
        detail_window.geometry(f"+{x}+{y}")
        
        # 创建内容框架
        content_frame = ttk.Frame(detail_window, padding="15")
        content_frame.pack(fill=tk.BOTH, expand=True)
        
        # 显示详情并添加复制按钮
        fields = [
            ("名称:", data[1] if data[1] else "无"),
            ("网址:", data[2] if data[2] else "无"),
            ("用户名:", data[3] if data[3] else "无"),
            ("密码:", decrypted_password),
            ("分类:", data[7] if data[7] else "未分类")
        ]
        
        # 添加字段和复制按钮
        for i, (label_text, value) in enumerate(fields):
            # 标签
            ttk.Label(content_frame, text=label_text, font=("Arial", 10, "bold")).grid(
                row=i, column=0, sticky=tk.W, pady=8, padx=(0, 10))
            
            # 值显示
            value_var = tk.StringVar(value=value)
            value_entry = ttk.Entry(content_frame, textvariable=value_var, width=30, state="readonly")
            value_entry.grid(row=i, column=1, sticky=tk.W, pady=8)
            
            # 复制按钮
            copy_btn = ttk.Button(
                content_frame, 
                text="复制", 
                command=lambda v=value: self.copy_to_clipboard(v, detail_window)
            )
            copy_btn.grid(row=i, column=2, padx=10)
        
        # 备注
        ttk.Label(content_frame, text="备注:", font=("Arial", 10, "bold")).grid(
            row=5, column=0, sticky=tk.NW, pady=8, padx=(0, 10))
        
        notes = data[5] if data[5] else "无"
        notes_text = tk.Text(content_frame, height=4, width=30, state="normal")
        notes_text.grid(row=5, column=1, sticky=tk.W, pady=8)
        notes_text.insert(tk.END, notes)
        notes_text.config(state="disabled")
        
        # 备注复制按钮
        copy_notes_btn = ttk.Button(
            content_frame, 
            text="复制", 
            command=lambda v=notes: self.copy_to_clipboard(v, detail_window)
        )
        copy_notes_btn.grid(row=5, column=2, padx=10, pady=8)
        
        # 关闭按钮
        ttk.Button(
            content_frame, 
            text="关闭", 
            command=detail_window.destroy
        ).grid(row=6, column=0, columnspan=3, pady=15)
    
    def copy_to_clipboard(self, text, parent_window):
        """将文本复制到剪贴板并显示提示"""
        self.root.clipboard_clear()
        self.root.clipboard_append(text)
        
        # 显示临时提示
        tooltip = tk.Toplevel(parent_window)
        tooltip.overrideredirect(True)  # 无边框
        tooltip.geometry("+%d+%d" % (
            parent_window.winfo_rootx() + parent_window.winfo_width() // 2 - 50,
            parent_window.winfo_rooty() + parent_window.winfo_height() // 2 - 15
        ))
        
        ttk.Label(tooltip, text="已复制到剪贴板", background="#e0f0e0", padding=5).pack()
        
        # 2秒后自动关闭提示
        self.root.after(2000, tooltip.destroy)
    
    def add_category(self):
        """添加新分类，支持选择父分类（一级或二级）"""
        # 获取所有一级分类作为父分类选项
        self.cursor.execute("SELECT id, name FROM categories WHERE parent_id IS NULL ORDER BY name")
        main_categories = self.cursor.fetchall()
        parent_options = [("无 (一级分类)", None)] + [(name, id) for id, name in main_categories]
        
        # 创建添加分类对话框
        dialog = CategoryDialog(self.root, "添加分类", parent_options)
        if dialog.result:
            name, parent_id = dialog.result
            
            if name and name.strip():
                name = name.strip()
                try:
                    # 插入新分类
                    self.cursor.execute(
                        "INSERT INTO categories (name, parent_id) VALUES (?, ?)", 
                        (name, parent_id)
                    )
                    self.conn.commit()
                    
                    # 重新加载分类并显示成功消息
                    if self.load_categories():
                        messagebox.showinfo("成功", f"分类 '{name}' 添加成功")
                except sqlite3.IntegrityError:
                    messagebox.showerror("错误", f"该分类下已存在 '{name}' 子分类")
                except Exception as e:
                    self.conn.rollback()
                    messagebox.showerror("错误", f"添加分类失败: {str(e)}")
    
    def edit_category(self):
        """编辑选中的分类"""
        selected_items = self.category_tree.selection()
        if not selected_items or selected_items[0] == self.all_item:
            messagebox.showwarning("警告", "请先选择一个分类")
            return
        
        selected_item = selected_items[0]
        category_name = self.category_tree.item(selected_item, "text")
        category_id = self.category_ids.get(selected_item)
        
        if not category_id or category_id == "all":
            messagebox.showwarning("警告", "请选择一个有效的分类")
            return
        
        # 获取该分类的父分类
        self.cursor.execute("SELECT parent_id FROM categories WHERE id = ?", (category_id,))
        result = self.cursor.fetchone()
        if not result:
            messagebox.showerror("错误", "未找到分类数据")
            return
        current_parent_id = result[0]
        
        # 获取所有一级分类作为父分类选项
        self.cursor.execute("SELECT id, name FROM categories WHERE parent_id IS NULL ORDER BY name")
        main_categories = self.cursor.fetchall()
        parent_options = [("无 (一级分类)", None)] + [(name, id) for id, name in main_categories]
        
        # 过滤掉自身作为父分类的选项
        parent_options = [opt for opt in parent_options if opt[1] != category_id]
        
        # 创建编辑分类对话框
        dialog = CategoryDialog(
            self.root, "编辑分类", parent_options,
            initial_name=category_name,
            initial_parent_id=current_parent_id
        )
        
        if dialog.result:
            new_name, new_parent_id = dialog.result
            
            if new_name and new_name.strip():
                new_name = new_name.strip()
                try:
                    self.cursor.execute(
                        "UPDATE categories SET name = ?, parent_id = ? WHERE id = ?", 
                        (new_name, new_parent_id, category_id)
                    )
                    self.conn.commit()
                    self.load_categories()
                    self.load_passwords()
                    messagebox.showinfo("成功", "分类修改成功")
                except sqlite3.IntegrityError:
                    messagebox.showerror("错误", f"该分类下已存在 '{new_name}' 子分类")
                except Exception as e:
                    self.conn.rollback()
                    messagebox.showerror("错误", f"修改分类失败: {str(e)}")
    
    def delete_category(self):
        """删除选中的分类"""
        selected_items = self.category_tree.selection()
        if not selected_items or selected_items[0] == self.all_item:
            messagebox.showwarning("警告", "请先选择一个分类")
            return
        
        selected_item = selected_items[0]
        category_name = self.category_tree.item(selected_item, "text")
        category_id = self.category_ids.get(selected_item)
        
        if not category_id or category_id == "all":
            messagebox.showwarning("警告", "请选择一个有效的分类")
            return
        
        # 检查是否有子分类
        self.cursor.execute("SELECT COUNT(*) FROM categories WHERE parent_id = ?", (category_id,))
        sub_category_count = self.cursor.fetchone()[0]
        
        if sub_category_count > 0:
            messagebox.showwarning("警告", f"分类 '{category_name}' 下还有 {sub_category_count} 个子分类，请先删除它们")
            return
        
        # 检查是否有密码使用该分类
        self.cursor.execute("SELECT COUNT(*) FROM passwords WHERE category_id = ?", (category_id,))
        password_count = self.cursor.fetchone()[0]
        
        if password_count > 0:
            if not messagebox.askyesno("确认", 
                f"分类 '{category_name}' 下还有 {password_count} 个密码，\n"
                f"删除后这些密码将需要重新分类。\n"
                f"确定要继续吗？"):
                return
        
        if messagebox.askyesno("确认", f"确定要删除分类 '{category_name}' 吗？"):
            try:
                # 删除分类
                self.cursor.execute("DELETE FROM categories WHERE id = ?", (category_id,))
                self.conn.commit()
                
                # 重新加载数据
                self.load_categories()
                self.load_passwords()
                messagebox.showinfo("成功", "分类已删除")
            except Exception as e:
                self.conn.rollback()
                messagebox.showerror("错误", f"删除分类失败: {str(e)}")
    
    def generate_password(self):
        """生成随机密码"""
        # 密码长度
        length = 16
        
        # 字符集
        lowercase = string.ascii_lowercase
        uppercase = string.ascii_uppercase
        digits = string.digits
        symbols = "!@#$%^&*()_-+=[]{}|;:,.<>?`~"
        
        # 确保密码包含各种类型的字符
        password = [
            random.choice(lowercase),
            random.choice(uppercase),
            random.choice(digits),
            random.choice(symbols)
        ]
        
        # 填充剩余字符
        all_chars = lowercase + uppercase + digits + symbols
        password += [random.choice(all_chars) for _ in range(length - 4)]
        
        # 打乱顺序
        random.shuffle(password)
        
        # 转换为字符串
        generated_password = ''.join(password)
        
        # 显示生成的密码
        messagebox.showinfo("生成密码", f"生成的密码:\n{generated_password}\n\n已复制到剪贴板")
        
        # 复制到剪贴板
        self.root.clipboard_clear()
        self.root.clipboard_append(generated_password)
    
    def show_environment_info(self):
        """显示环境信息用于调试"""
        info = f"操作系统: {platform.system()} {platform.release()}\n"
        info += f"Python版本: {sys.version.split()[0]}\n"
        info += f"数据库路径: {self.db_path}\n"
        info += f"密钥文件路径: {self.key_file}"
        
        # 在控制台输出环境信息
        print("="*50)
        print("运行环境信息:")
        print(info)
        print("="*50)


class PasswordDialog(tk.Toplevel):
    """密码添加/编辑对话框，支持选择二级分类"""
    def __init__(self, parent, title, manager, **kwargs):
        super().__init__(parent)
        self.parent = parent
        self.manager = manager
        self.title(title)
        self.geometry("400x380")
        self.resizable(False, False)
        self.result = None
        
        # 居中显示
        self.transient(parent)
        self.grab_set()
        self.update_idletasks()
        width = self.winfo_width()
        height = self.winfo_height()
        x = (parent.winfo_width() // 2) - (width // 2) + parent.winfo_x()
        y = (parent.winfo_height() // 2) - (height // 2) + parent.winfo_y()
        self.geometry(f"+{x}+{y}")
        
        # 存储参数
        self.kwargs = kwargs
        
        # 创建界面
        self.create_widgets()
        
        # 等待用户输入
        self.parent.wait_window(self)
    
    def create_widgets(self):
        """创建对话框组件"""
        frame = ttk.Frame(self, padding="10")
        frame.pack(fill=tk.BOTH, expand=True)
        
        # 名称
        ttk.Label(frame, text="名称*:").grid(row=0, column=0, sticky=tk.W, pady=5)
        self.name_var = tk.StringVar(value=self.kwargs.get("name", ""))
        ttk.Entry(frame, textvariable=self.name_var, width=40).grid(row=0, column=1, pady=5)
        
        # 网址
        ttk.Label(frame, text="网址:").grid(row=1, column=0, sticky=tk.W, pady=5)
        self.url_var = tk.StringVar(value=self.kwargs.get("url", ""))
        ttk.Entry(frame, textvariable=self.url_var, width=40).grid(row=1, column=1, pady=5)
        
        # 用户名
        ttk.Label(frame, text="用户名:").grid(row=2, column=0, sticky=tk.W, pady=5)
        self.username_var = tk.StringVar(value=self.kwargs.get("username", ""))
        ttk.Entry(frame, textvariable=self.username_var, width=40).grid(row=2, column=1, pady=5)
        
        # 密码
        ttk.Label(frame, text="密码*:").grid(row=3, column=0, sticky=tk.W, pady=5)
        self.password_var = tk.StringVar(value=self.kwargs.get("password", ""))
        password_frame = ttk.Frame(frame)
        password_frame.grid(row=3, column=1, pady=5, sticky=tk.W)
        ttk.Entry(password_frame, textvariable=self.password_var, width=30).pack(side=tk.LEFT)
        ttk.Button(password_frame, text="生成", command=self.generate_password).pack(side=tk.LEFT, padx=5)
        
        # 分类（支持二级）
        ttk.Label(frame, text="分类*:").grid(row=4, column=0, sticky=tk.W, pady=5)
        self.category_id = tk.StringVar(value=self.kwargs.get("category_id", ""))
        
        # 创建分类下拉框
        self.category_combo = ttk.Combobox(frame, textvariable=self.category_id, state="readonly", width=37)
        self.category_combo.grid(row=4, column=1, pady=5)
        
        # 加载分类
        self.load_categories()
        
        # 备注
        ttk.Label(frame, text="备注:").grid(row=5, column=0, sticky=tk.NW, pady=5)
        self.notes_widget = tk.Text(frame, height=4, width=30)
        self.notes_widget.grid(row=5, column=1, pady=5)
        self.notes_widget.insert(tk.END, self.kwargs.get("notes", ""))
        
        # 按钮
        button_frame = ttk.Frame(frame)
        button_frame.grid(row=6, column=0, columnspan=2, pady=10)
        
        ttk.Button(button_frame, text="确定", command=self.on_ok).pack(side=tk.LEFT, padx=10)
        ttk.Button(button_frame, text="取消", command=self.on_cancel).pack(side=tk.LEFT)
    
    def load_categories(self):
        """加载所有分类（包括二级分类）到下拉框"""
        # 存储分类ID和显示文本的映射
        self.category_mapping = {}
        # 存储下拉框选项
        combo_values = []
        
        try:
            # 获取一级分类
            self.manager.cursor.execute("SELECT id, name FROM categories WHERE parent_id IS NULL ORDER BY name")
            main_categories = self.manager.cursor.fetchall()
            
            # 添加一级分类到下拉框
            for main_id, main_name in main_categories:
                self.category_mapping[str(main_id)] = main_name
                # 获取二级分类
                self.manager.cursor.execute("SELECT id, name FROM categories WHERE parent_id = ? ORDER BY name", (main_id,))
                sub_categories = self.manager.cursor.fetchall()
                
                for sub_id, sub_name in sub_categories:
                    display_text = f"  └ {sub_name}"  # 缩进显示二级分类
                    self.category_mapping[str(sub_id)] = f"{main_name} > {sub_name}"
                    combo_values.append(display_text)
                
                combo_values.append(main_name)
            
            # 设置下拉框选项
            self.category_combo['values'] = combo_values
            
            # 如果有初始分类ID，设置选中状态
            initial_id = self.kwargs.get("category_id")
            if initial_id:
                # 找到对应的显示文本
                for cat_id, cat_name in self.category_mapping.items():
                    if int(cat_id) == initial_id:
                        # 找到对应的显示文本
                        for value in combo_values:
                            if cat_name.endswith(value.strip(" └")):
                                self.category_combo.set(value)
                                break
                        break
        except Exception as e:
            messagebox.showerror("加载分类错误", f"无法加载分类列表: {str(e)}")
    
    def generate_password(self):
        """生成随机密码"""
        length = 16
        lowercase = string.ascii_lowercase
        uppercase = string.ascii_uppercase
        digits = string.digits
        symbols = "!@#$%^&*()_-+=[]{}|;:,.<>?`~"
        
        password = [
            random.choice(lowercase),
            random.choice(uppercase),
            random.choice(digits),
            random.choice(symbols)
        ]
        
        all_chars = lowercase + uppercase + digits + symbols
        password += [random.choice(all_chars) for _ in range(length - 4)]
        random.shuffle(password)
        
        self.password_var.set(''.join(password))
    
    def on_ok(self):
        """确认按钮事件"""
        name = self.name_var.get().strip()
        password = self.password_var.get().strip()
        
        if not name or not password:
            messagebox.showerror("错误", "名称和密码不能为空")
            return
        
        # 获取选中的分类
        selected_text = self.category_combo.get()
        if not selected_text:
            messagebox.showerror("错误", "请选择分类")
            return
        
        # 查找选中的分类ID
        selected_id = None
        for cat_id, cat_name in self.category_mapping.items():
            if selected_text.strip(" └") in cat_name:
                selected_id = cat_id
                break
        
        if not selected_id:
            messagebox.showerror("错误", "无法获取分类信息")
            return
        
        self.result = (
            name,
            self.url_var.get().strip(),
            self.username_var.get().strip(),
            password,
            self.notes_widget.get("1.0", tk.END).strip(),
            selected_id
        )
        
        self.destroy()
    
    def on_cancel(self):
        """取消按钮事件"""
        self.destroy()


class CategoryDialog(tk.Toplevel):
    """分类添加/编辑对话框"""
    def __init__(self, parent, title, parent_options, initial_name="", initial_parent_id=None):
        super().__init__(parent)
        self.parent = parent
        self.title(title)
        self.geometry("300x150")
        self.resizable(False, False)
        self.result = None
        self.parent_options = parent_options
        
        # 居中显示
        self.transient(parent)
        self.grab_set()
        self.update_idletasks()
        width = self.winfo_width()
        height = self.winfo_height()
        x = (parent.winfo_width() // 2) - (width // 2) + parent.winfo_x()
        y = (parent.winfo_height() // 2) - (height // 2) + parent.winfo_y()
        self.geometry(f"+{x}+{y}")
        
        # 创建界面
        self.create_widgets(initial_name, initial_parent_id)
        
        # 等待用户输入
        self.parent.wait_window(self)
    
    def create_widgets(self, initial_name, initial_parent_id):
        """创建对话框组件"""
        frame = ttk.Frame(self, padding="10")
        frame.pack(fill=tk.BOTH, expand=True)
        
        # 分类名称
        ttk.Label(frame, text="分类名称*:").grid(row=0, column=0, sticky=tk.W, pady=5)
        self.name_var = tk.StringVar(value=initial_name)
        ttk.Entry(frame, textvariable=self.name_var, width=25).grid(row=0, column=1, pady=5)
        
        # 父分类
        ttk.Label(frame, text="父分类:").grid(row=1, column=0, sticky=tk.W, pady=5)
        self.parent_var = tk.StringVar()
        parent_combo = ttk.Combobox(frame, textvariable=self.parent_var, state="readonly", width=25)
        parent_combo.grid(row=1, column=1, pady=5)
        
        # 填充父分类选项
        parent_names = [opt[0] for opt in self.parent_options]
        parent_combo['values'] = parent_names
        
        # 设置初始父分类
        if initial_parent_id is not None:
            for name, id in self.parent_options:
                if id == initial_parent_id:
                    self.parent_var.set(name)
                    break
        else:
            self.parent_var.set(parent_names[0])  # 默认选择"无 (一级分类)"
        
        # 按钮
        button_frame = ttk.Frame(frame)
        button_frame.grid(row=2, column=0, columnspan=2, pady=10)
        
        ttk.Button(button_frame, text="确定", command=self.on_ok).pack(side=tk.LEFT, padx=10)
        ttk.Button(button_frame, text="取消", command=self.on_cancel).pack(side=tk.LEFT)
        
        self.parent_combo = parent_combo
    
    def on_ok(self):
        """确认按钮事件"""
        name = self.name_var.get().strip()
        if not name:
            messagebox.showerror("错误", "分类名称不能为空")
            return
        
        # 找到选中的父分类ID
        selected_parent_name = self.parent_var.get()
        parent_id = None
        for name_opt, id_opt in self.parent_options:
            if name_opt == selected_parent_name:
                parent_id = id_opt
                break
        
        self.result = (name, parent_id)
        self.destroy()
    
    def on_cancel(self):
        """取消按钮事件"""
        self.destroy()


if __name__ == "__main__":
    root = tk.Tk()
    app = PasswordManager(root)
    root.mainloop()
    